package cn.sxgan.utils;


import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.text.ParseException;
import java.util.Date;
import java.lang.management.ManagementFactory;
import java.security.SecureRandom;
import java.text.SimpleDateFormat;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Period;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.regex.Pattern;

public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
    private static final SecureRandom RANDOM = new SecureRandom();
    public static String YYYY_MM_DD = "yyyy-MM-dd";
    
    
    @Getter
    @AllArgsConstructor
    public enum DatePatternEnum {
        YYYY("yyyy"),
        YYYY_MM("yyyy-MM"),
        YYYY_MM_DD("yyyy-MM-dd"),
        YYYY_MM_DD_PATH("yyyy/MM/dd"),
        YYYYMMDD("yyyyMMdd"),
        YYYYMMDDHHMMSS("yyyyMMddHHmmss"),
        YYYY_MM_DD_HH_MM_SS("yyyy-MM-dd HH:mm:ss");
        
        private final String pattern;
    }
    
    private static final String[] parsePatterns = {
            "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
            "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
            "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
    
    /**
     * 获取当前Date型日期
     *
     * @return Date() 当前日期
     */
    public static Date getNowDate() {
        return new Date();
    }
    
    /**
     * 获取当前日期字符串,格式为yyyy-MM-dd
     *
     * @return String 当前日期
     */
    public static String getDateStr() {
        return dateTimeNow(DatePatternEnum.YYYY_MM_DD.pattern);
    }
    
    /**
     * 获取当前日期,格式为yyyy-MM-dd HH:mm:ss
     *
     * @return String 当前日期
     */
    public static String getTime() {
        return dateTimeNow(DatePatternEnum.YYYY_MM_DD_HH_MM_SS.pattern);
    }
    
    /**
     * 获取当前时间,格式为yyyyMMddHHmmss
     *
     * @return String 当前时间
     */
    public static String dateTimeNow() {
        return dateTimeNow(DatePatternEnum.YYYYMMDDHHMMSS.pattern);
    }
    
    /**
     * 当前日期路径格式， 即年/月/日，如2018/08/08
     *
     * @return String 当前日期
     */
    public static String datePath() {
        return dateTimeNow(DatePatternEnum.YYYY_MM_DD_PATH.pattern);
    }
    
    /**
     * 获取当前时间
     *
     * @param datePattern 时间格式
     * @return String 当前时间
     */
    public static String dateTimeNow(String datePattern) {
        return parseDateToStr(new Date(), datePattern);
    }
    
    
    /**
     * 传入时间获取对应日期字符串，转换格式为yyyy-MM-dd
     *
     * @param date 传入时间
     * @return String 日期字符串
     */
    public static String dateTime(final Date date) {
        return parseDateToStr(date, DatePatternEnum.YYYY_MM_DD.pattern);
    }
    
    /**
     * 获取当前日期,格式为yyyy-MM-dd
     *
     * @param datePattern 日期格式
     * @param date        传入时间
     * @return String 日期字符串
     */
    public static String parseDateToStr(final Date date, String datePattern) {
        return new SimpleDateFormat(datePattern).format(date);
    }
    
    
    /**
     * 将时间字符串转换为Date类型
     *
     * @param dataPattern 日期格式
     * @param ts          时间字符串
     * @return Date 日期
     */
    public static Date parseStrToDate(final String ts, String dataPattern) {
        try {
            return new SimpleDateFormat(dataPattern).parse(ts);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }
    
    /**
     * 获取服务器启动时间
     *
     * @return Date 服务器启动时间
     */
    public static Date getServerStartDate() {
        long time = ManagementFactory.getRuntimeMXBean().getStartTime();
        return new Date(time);
    }
    
    /**
     * 计算两个时间相差天数
     *
     * @param date1 时间1
     * @param date2 时间2
     * @return int 天数
     */
    public static int differentDaysByMillisecond(Date date1, Date date2) {
        return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24)));
    }
    
    /**
     * 计算时间差，精确到分钟
     *
     * @param endDate   最后时间
     * @param startTime 开始时间
     * @return 时间差(天 / 小时 / 分钟)
     */
    public static String timeDistance(Date endDate, Date startTime) {
        long nd = 1000 * 24 * 60 * 60;
        long nh = 1000 * 60 * 60;
        long nm = 1000 * 60;
        // long ns = 1000;
        // 获得两个时间的毫秒时间差异
        long diff = endDate.getTime() - startTime.getTime();
        // 计算差多少天
        long day = diff / nd;
        // 计算差多少小时
        long hour = diff % nd / nh;
        // 计算差多少分钟
        long min = diff % nd % nh / nm;
        // 计算差多少秒//输出结果
        // long sec = diff % nd % nh % nm / ns;
        return day + "天" + hour + "小时" + min + "分钟";
    }
    
    /**
     * LocalDateTime转Date
     *
     * @param temporalAccessor LocalDateTime
     * @return Date
     */
    public static Date toDate(LocalDateTime temporalAccessor) {
        ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
        return Date.from(zdt.toInstant());
    }
    
    /**
     * LocalDate转Date
     *
     * @param temporalAccessor LocalDate
     * @return Date
     */
    public static Date toDate(LocalDate temporalAccessor) {
        LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
        return toDate(localDateTime);
    }
    
    /**
     * Date 转 LocalDateTime（系统默认时区）
     */
    public static LocalDateTime dateToLocalDateTime(Date date) {
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    }
    
    /**
     * Date 转 LocalDate（系统默认时区）
     */
    public static LocalDate dateToLocalDate(Date date) {
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    }
    
    /**
     * LocalDateTime 转 Instant（系统默认时区）
     */
    public static Instant localDateTimeToInstant(LocalDateTime localDateTime) {
        return localDateTime.atZone(ZoneId.systemDefault()).toInstant();
    }
    
    /**
     * Instant 转 LocalDateTime（系统默认时区）
     */
    public static LocalDateTime instantToLocalDateTime(Instant instant) {
        return instant.atZone(ZoneId.systemDefault()).toLocalDateTime();
    }
    
    /**
     * LocalDate 转 LocalDateTime（时间设为00:00:00）
     */
    public static LocalDateTime localDateToLocalDateTime(LocalDate localDate) {
        return localDate.atStartOfDay();
    }
    
    /**
     * 带时区转换的版本（示例）
     * LocalDateTime 转 Date（指定时区）
     */
    public static Date localDateTimeToDate(LocalDateTime localDateTime, ZoneId zoneId) {
        return Date.from(localDateTime.atZone(zoneId).toInstant());
    }
    
    /**
     * 带时区转换的版本（示例）
     * Date 转 LocalDateTime（指定时区）
     */
    public static LocalDateTime dateToLocalDateTime(Date date, ZoneId zoneId) {
        return date.toInstant().atZone(zoneId).toLocalDateTime();
    }
    
    
    /**
     * 获取当前时间到午夜零点的秒数(今天剩余秒数)
     *
     * @return 今天剩余秒数
     */
    public static Integer getSecondsToMidnight() {
        Calendar curDate = Calendar.getInstance();
        Calendar tommorowDate = new GregorianCalendar(curDate
                .get(Calendar.YEAR), curDate.get(Calendar.MONTH), curDate
                .get(Calendar.DATE) + 1, 0, 0, 0);
        return (int) (tommorowDate.getTimeInMillis() - curDate.getTimeInMillis()) / 1000;
    }
    
    /**
     * 判断某一时间是否为指定范围内
     *
     * @param date  时间
     * @param begin 开始时间
     * @param end   结束时间
     * @return true|false
     */
    public static boolean isBetween(Date date, Date begin, Date end) {
        return date.after(begin) && date.before(end);
    }
    
    /**
     * 判断某一时间是否为指定范围内
     *
     * @param dateStr  时间字符串
     * @param beginStr 开始时间字符串
     * @param endStr   结束时间字符串
     * @param pattern  时间格式
     * @return true|false
     */
    public static boolean isBetween(String dateStr, String beginStr, String endStr, String pattern) {
        Date date = DateUtils.parseStrToDate(dateStr, pattern);
        Date beginDate = DateUtils.parseStrToDate(beginStr, pattern);
        Date endDate = DateUtils.parseStrToDate(endStr, pattern);
        return isBetween(date, beginDate, endDate);
    }
    
    /**
     * 获取当前日期时间的字符串数组，数组第0位为日期，第1位为时间
     *
     * @return String[]
     */
    public static String[] getDateAndTime() {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd@HH:mm:ss");
        String format = simpleDateFormat.format(new Date());
        return format.split("@");
    }
    
    /**
     * 获取随机日期，默认为1949年至今，若大于当前日期则按当前日期执行
     *
     * @return 返回格式为yyyyMMdd的字符串日期
     */
    public static String getRandomDate() {
        int year = LocalDate.now().getYear();
        return getRandomDate(1949, year, DatePatternEnum.YYYYMMDD);
    }
    
    /**
     * 获取范围内随机日期
     *
     * @param beginYear 开始年份
     * @param endYear   结束年份
     * @param pattern   日期格式
     * @return 字符串日期
     */
    public static String getRandomDate(int beginYear, int endYear, DatePatternEnum pattern) {
        if (endYear < beginYear) {
            return "";
        }
        int addYear = endYear - beginYear;
        int year = RANDOM.nextInt(addYear + 1) + beginYear;// 1970年至今50年内的随机年份
        int curMonth = LocalDateTime.now().getMonthValue();
        int curDay = LocalDateTime.now().getDayOfMonth();
        
        int month = 12;
        int day = 1;
        if (year == endYear) {
            month = RANDOM.nextInt(curMonth) + 1;// 1月至当前月的随机月份
            day = RANDOM.nextInt(curDay) + 1;// 1月至当前月的随机月份
        } else {
            month = RANDOM.nextInt(12) + 1;// 1月至12月的随机月份
            Calendar calendar = new GregorianCalendar(year, month - 1, 1);
            int maxDay = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
            day = RANDOM.nextInt(maxDay) + 1;// 1日至当月最大日的随机日期
        }
        String monthStr = month < 10 ? "0" + month : month + "";
        String dayStr = day < 10 ? "0" + day : day + "";
        Date date = parseStrToDate(year + "-" + monthStr + "-" + dayStr, "yyyy-MM-dd");
        return parseDateToStr(date, pattern.getPattern());
    }
    
    public static String getAge(Date date) {
        LocalDate currentDate = LocalDate.now();
        String birthDateStr = parseDateToStr(date, "yyyy-MM-dd");
        String[] split = birthDateStr.split("-");
        LocalDate birthDate = LocalDate.of(Integer.parseInt(split[0]), Integer.parseInt(split[1]), Integer.parseInt(split[2]));
        // 计算年份差
        int age = Period.between(birthDate, currentDate).getYears();
        return String.valueOf(age);
    }
    
    /**
     * 根据时间往后推
     *
     * @param date     指定date
     * @param num      时间值
     * @param timeUnit 单位
     * @return date
     */
    public static Date dateToFuture(Date date, long num, ChronoUnit timeUnit) {
        Instant instant = date.toInstant();
        // 当前时间往后推num个时间单位
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
        LocalDateTime futureDateTime = localDateTime.plus(num, timeUnit);
        return toDate(futureDateTime);
    }
    
    /**
     * 日期转Cron表达式（忽略年份）
     *
     * @param localDateTime 日期时间
     * @return 标准6位Cron表达式
     */
    public static String localDateTimeToCron(LocalDateTime localDateTime) {
        return String.format("%d %d %d %d %d ?",
                localDateTime.getSecond(),
                localDateTime.getMinute(),
                localDateTime.getHour(),
                localDateTime.getDayOfMonth(),
                localDateTime.getMonthValue());
    }
    
    /**
     * Cron表达式转日期（补全当前年份）
     *
     * @param cron 标准6位Cron表达式
     * @return 补全年份的日期时间
     * @throws IllegalArgumentException 包含通配符/非法字段时抛出
     */
    public static LocalDateTime cronToLocalDateTime(String cron) {
        String[] fields = cron.split("\\s+");
        validateCronFormat(fields);
        
        int second = parseField(fields[0], "秒", 0, 59);
        int minute = parseField(fields[1], "分", 0, 59);
        int hour = parseField(fields[2], "时", 0, 23);
        int day = parseField(fields[3], "日", 1, 31);
        int month = parseField(fields[4], "月", 1, 12);
        
        if (!"?".equals(fields[5])) {
            throw new IllegalArgumentException("周字段必须为'?'");
        }
        
        int currentYear = LocalDateTime.now().getYear();
        return createDateTime(currentYear, month, day, hour, minute, second);
    }
    
    private static void validateCronFormat(String[] fields) {
        if (fields.length != 6) {
            throw new IllegalArgumentException("必须为6位Cron表达式");
        }
    }
    
    private static int parseField(String field, String name, int min, int max) {
        if (!Pattern.matches("^\\d+$", field)) {
            throw new IllegalArgumentException(name + "字段包含非法字符");
        }
        
        int value = Integer.parseInt(field);
        if (value < min || value > max) {
            throw new IllegalArgumentException(name + "字段超出有效范围");
        }
        return value;
    }
    
    private static LocalDateTime createDateTime(int year, int month, int day,
                                                int hour, int minute, int second) {
        try {
            return LocalDateTime.of(year, month, day, hour, minute, second);
        } catch (DateTimeException e) {
            throw new IllegalArgumentException("无效日期参数: " + e.getMessage());
        }
    }
    
   
    
    /**
     * 获取指定月份内的所有日期字符串
     *
     * @param input 输入的格式为 "yyyy-MM"
     * @return 包含所有日期字符串的列表
     */
    public static List<String> getMonthDates(String input) {
        List<String> dates = new ArrayList<>();
        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        
        try {
            // 解析输入的年份和月份
            YearMonth yearMonth = YearMonth.parse(input);
            int lengthOfMonth = yearMonth.lengthOfMonth();
            
            // 添加每月的每一天到列表
            for (int day = 1; day <= lengthOfMonth; day++) {
                String date = yearMonth.getYear() + "-"
                        + String.format("%02d", yearMonth.getMonthValue()) + "-"
                        + String.format("%02d", day);
                dates.add(date);
            }
        } catch (DateTimeParseException e) {
            System.err.println("Invalid input format. Expected format: yyyy-MM");
        } catch (Exception e) {
            System.err.println("Error occurred while generating dates: " + e.getMessage());
        }
        
        return dates;
    }
    
    // public static void main(String[] args) {
    //     Date date = new Date();
    //     List<String> monthDates = getMonthDates("2025-02");
    //     monthDates.forEach(System.out::println);
    //     // LocalDateTime localDateTime = dateToLocalDateTime(date);
    //     // System.out.println(localDateTimeToCron(localDateTime));
    //     // LocalDateTime localDateTime1 = cronToLocalDateTime(localDateTimeToCron(localDateTime));
    //     // Date date1 = toDate(localDateTime1);
    //     // System.out.println(parseDateToStr(date1, "yyyy-MM-dd HH:mm:ss"));
    //     // System.out.println(getAge(parseStrToDate("2023-09-12", "yyyy-MM-dd")));
    // }
}